using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;
using submgr = Microsoft.Dss.Services.SubscriptionManager;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using System.Drawing;
using System.Windows.Forms;
using capt = Robotics.ImageCapt.Proxy;

namespace Robotics.ImgToHistService
{
    [Contract(Contract.Identifier)]
    [DisplayName("ImgToHistService")]
    [Description("ImgToHistService service (no description provided)")]
    class ImgToHistService : DsspServiceBase
    {
        /// <summary>
        /// Service state
        /// </summary>
        [ServiceState]
        [InitialStatePartner(Optional = true, ServiceUri = "ImgToHistService.Config.xml")]
        ImgToHistServiceState _state = new ImgToHistServiceState();

        /// <summary>
        /// Main service port
        /// </summary>
        [ServicePort("/ImgToHistService", AllowMultipleInstances = true)]
        ImgToHistServiceOperations _mainPort = new ImgToHistServiceOperations();

        /// <summary>
        /// Internal port to update private state.
        /// </summary>
        private ImgToHistServiceOperations _internalPort = new ImgToHistServiceOperations();

        [Partner("ImageCapture", Contract = capt.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)]
        capt.ImageCaptOperations _imgCaptPort = new capt.ImageCaptOperations();
        capt.ImageCaptOperations _imgCaptNotify = new capt.ImageCaptOperations();

        [SubscriptionManagerPartner]
        submgr.SubscriptionManagerPort _submgrPort = new submgr.SubscriptionManagerPort();

        /// <summary>
        /// Service constructor
        /// </summary>
        public ImgToHistService(DsspServiceCreationPort creationPort)
            : base(creationPort)
        {
        }

        /// <summary>
        /// Service start
        /// </summary>
        protected override void Start()
        {

            // 
            // Add service specific initialization here
            // 

            base.Start();
            Console.WriteLine("Starting Service...");
            LogInfo("Image Histogram Calculation starting...");

            // Make sure that we have an initial state!
            if (_state == null)
            {
                _state = new ImgToHistServiceState();
            }

            // Initialize state variables
            _state.Dims = 2;
            
            _state.BinSizes = new int[2];
            _state.BinSizes[0] = 30;
            _state.BinSizes[1] = 32;

            _state.Ranges = new RangeF[2];
            _state.Ranges[0] = new RangeF(0, 180);
            _state.Ranges[1] = new RangeF(0, 255);

            SaveState(_state);

            SpawnIterator(SubscribeToCapture);

            MainPortInterleave.CombineWith(
                Arbiter.Interleave(
                    new TeardownReceiverGroup(),
                    new ExclusiveReceiverGroup(
                        Arbiter.Receive<NotifyHistCalculation>(true, _internalPort, HistCalculationHandler),
                        Arbiter.Receive<capt.NotifyImageCapture>(false, _imgCaptNotify, ImageNotificationHandler)
                    ),
                    new ConcurrentReceiverGroup
                        (
                            Arbiter.Receive<CalcHistFromImage>(true, _internalPort, CalcHistFromImageHandler)
                        )
            ));
        }

        public void ImageNotificationHandler(capt.NotifyImageCapture update)
        {
            Console.WriteLine("Image from camera received");
            LogInfo("Image from camera received");
            Image<Bgr, byte> img = new Image<Bgr, byte>(update.Body.CaptureResImage);
            _internalPort.Post(new CalcHistFromImage(new CalcHistFromImageRequest(img.Data)));
        }

        // Handler for subscribing to Image Capture Service
        IEnumerator<ITask> SubscribeToCapture()
        {
            Fault fault = null;
            SubscribeResponseType s;

            // Subscribe to the webcam
            capt.Subscribe subscribe = new capt.Subscribe();
            subscribe.NotificationPort = _imgCaptNotify;

            _imgCaptPort.Post(subscribe);

            yield return Arbiter.Choice(
                subscribe.ResponsePort,
                delegate(SubscribeResponseType success)
                { s = success; },
                delegate(Fault f)
                {
                    fault = f;
                }
            );

            if (fault != null)
            {
                LogError(null, "Failed to subscribe to image capture", fault);
                Console.WriteLine("Subscription to image capture failed");
                yield break;
            }
            else
            {
                LogInfo("Subscription to image cature success");
                Console.WriteLine("Subscription to image capture success");
            }

            yield break;

        }

        public void HistCalculationHandler(NotifyHistCalculation update)
        {
            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
            SendNotification(_submgrPort, update);
        }

        /// <summary>
        /// Handles Subscribe messages
        /// </summary>
        /// <param name="subscribe">the subscribe request</param>
        [ServiceHandler]
        public void SubscribeHandler(Subscribe subscribe)
        {
            SubscribeHelper(_submgrPort, subscribe.Body, subscribe.ResponsePort);
        }

        ///// <summary>
        ///// Histogram and Input Image Result
        ///// </summary>
        //public class ImageHistResult
        //{
        //    /// <summary>
        //    /// Image
        //    /// </summary>
        //    public Image<Bgr, byte> inputImage;
        //    /// <summary>
        //    /// Histogram
        //    /// </summary>
        //    public DenseHistogram histResult;
        //}

        /// <summary>
        /// Calculate Histogram From Image Handler
        /// </summary>
        /// <param name="request">CalcHistFromImageRequest</param>
        /// <returns></returns>
        public void CalcHistFromImageHandler(CalcHistFromImage request)
        {
            if (_state == null)
                request.ResponsePort.Post(new Fault());
            else
            {
                request.ResponsePort.Post(DefaultUpdateResponseType.Instance);
                Image<Bgr, byte> img = new Image<Bgr, byte>(request.Body.ImageForHist);
                Image<Hsv, byte> himg = img.Convert<Hsv, byte>();
                Image<Gray, byte> hp = new Image<Gray, byte>(img.Size);
                hp.Ptr = CvInvoke.cvCreateImage(img.Size, Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 1);
                Image<Gray, byte> sp = new Image<Gray, byte>(img.Size);
                sp.Ptr = CvInvoke.cvCreateImage(img.Size, Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 1);
                Image<Gray, byte> vp = new Image<Gray, byte>(img.Size);
                vp.Ptr = CvInvoke.cvCreateImage(img.Size, Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 1);
                Image<Gray, byte>[] planes = new Image<Gray, byte>[2];
                planes[0] = hp;
                planes[1] = sp;
                CvInvoke.cvCvtPixToPlane(himg.Ptr, hp.Ptr, sp.Ptr, vp.Ptr, IntPtr.Zero);

                DenseHistogram hist = new DenseHistogram(_state.BinSizes, _state.Ranges);
                hist.Calculate(planes, false, null);
                hist.Normalize(1.0);

                //ImageHistResult imgHist = new ImageHistResult();
                //imgHist.inputImage = img;
                //imgHist.histResult = hist;

                _internalPort.Post(new NotifyHistCalculation(new HistResult(hist.MCvHistogram)));

                Image<Bgr, byte> hist_img = new Image<Bgr, byte>(new Size(300, 320));
                CvInvoke.cvZero(hist_img.Ptr);
                float max_val = 0;
                float min_val = 0;
                int[] minloc = new int[2];
                int[] maxloc = new int[2];
                hist.MinMax(out min_val, out max_val, out minloc, out maxloc);
                for (int h = 0; h < _state.BinSizes[0]; h++)
                {
                    for (int s = 0; s < _state.BinSizes[1]; s++)
                    {
                        double bin_val = CvInvoke.cvQueryHistValue_2D(hist.Ptr, h, s);
                        int intensity = (int)(bin_val * 255 / max_val);
                        MCvScalar col = new MCvScalar(intensity, intensity, intensity);
                        CvInvoke.cvRectangle(hist_img.Ptr, new Point(h * 10, s * 10), new Point((h + 1) * 10 - 1, (s + 1) * 10 - 1), col, -1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, 0);
                    }
                }

                ImageViewer iv = new ImageViewer();
                iv.Image = hist_img;
                iv.ShowDialog();
            }
        }
    }
}


